Skip to main content

Integrating with FMOD

Sometimes you migh opt to use Reactional in tandem with a middleware such as FMOD.

Reactional generates a PCM buffer, and this buffer can be piped into any audio system.

Programmer Instrument

The easiest way to accomplish this is with a Programmer instrument, in FMOD Studio.

  • Create a new event (Reactional/ReactionalInput)
  • Add a programmer instrument.
  • Select and make sure it is set to Async, and Looping
  • Add a Sustain point
  • Right click on the event output meter and change output mode to Stereo (else it might default to mono)

Fmod Settings

ReactionalFmodCallback

In order to send audio into this programmer instrument, please implement this script in Unity.

using System;
using UnityEngine;
using System.Runtime.InteropServices;
using FMOD.Studio;
using FMODUnity;
using Reactional.Core;
using FMOD;

class ReactionalFmodCallback : MonoBehaviour
{
public FMODUnity.EventReference eventName;

FMOD.SOUND_PCMREAD_CALLBACK pcmReadCallback;
FMOD.Studio.EVENT_CALLBACK reactionalEventCallback;
private FMOD.CREATESOUNDEXINFO re_exinfo;
private FMOD.Sound reactionalOuput;
FMOD.Studio.EventInstance reactionalInstance;

void Start()
{
if (ReactionalEngine.Instance.AudioOutputMode == Reactional.Setup.AudioOutputMode.Unity)
{
return;
}
// get buffer size and samplerate
uint bufferSize;
FMODUnity.RuntimeManager.CoreSystem.getDSPBufferSize(out bufferSize, out _);
UnityEngine.Debug.Log("Buffer size: " + bufferSize);
int samplerate;
FMODUnity.RuntimeManager.CoreSystem.getSoftwareFormat(out samplerate, out _, out _);

//Create sound for programmer instrument
re_exinfo.cbsize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
re_exinfo.numchannels = 2;
re_exinfo.format = FMOD.SOUND_FORMAT.PCMFLOAT;
re_exinfo.defaultfrequency = samplerate;
re_exinfo.decodebuffersize = bufferSize*4;
re_exinfo.length = bufferSize;
pcmReadCallback = new FMOD.SOUND_PCMREAD_CALLBACK(PcmReadCallback);
re_exinfo.pcmreadcallback = pcmReadCallback;
FMODUnity.RuntimeManager.CoreSystem.createStream(re_exinfo.userdata, FMOD.MODE.LOOP_NORMAL | FMOD.MODE.OPENUSER | FMOD.MODE._2D, ref re_exinfo, out reactionalOuput);

// Initialize Reactional audio settings
Reactional.Setup.InitAudio((int)bufferSize,samplerate);

PlayMusic();
}

void PlayMusic()
{
//Initialize Event Instance
reactionalEventCallback = new FMOD.Studio.EVENT_CALLBACK(ReactionalEventCallback);
reactionalInstance = FMODUnity.RuntimeManager.CreateInstance(eventName);
reactionalInstance.setCallback(reactionalEventCallback);
reactionalInstance.start();
reactionalInstance.release();
}

private void OnDisable()
{
reactionalInstance.release();
reactionalInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
if (reactionalOuput.hasHandle())
{
reactionalOuput.release();
}
}

private void OnApplicationQuit()
{
reactionalInstance.release();
reactionalInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
}

FMOD.RESULT PcmReadCallback(IntPtr sound, IntPtr data, uint datalen)
{
float[] floatbuffer = new float[datalen/sizeof(float)];
if (Reactional.Setup.AllowPlay)
{
ReactionalEngine.Instance.engine.RenderInterleaved(48000, floatbuffer.Length/2, 2, floatbuffer);
}

Marshal.Copy(floatbuffer, 0, data, floatbuffer.Length);

return FMOD.RESULT.OK;
}

//Programmer Instrument Callback
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
FMOD.RESULT ReactionalEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
switch (type)
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
{
//get properties
PROGRAMMER_SOUND_PROPERTIES properties = (PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));

//Assign Sound
properties.sound = reactionalOuput.handle;

Marshal.StructureToPtr(properties, parameterPtr, false);
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
{
var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
var sound = new FMOD.Sound(parameter.sound);
sound.release();
break;
}
}
return FMOD.RESULT.OK;
}
}